﻿using System.Collections.Generic;
using PMatch.Formats.sPMatch;

namespace PMatch.Sampling
{
	/// <summary>
	/// Resultado de una busqueda de mejor ventana
	/// </summary>
	public class FindBestWindowResult
	{
		public int? PrefixLength { get; set; }
		public int? PostfixLength { get; set; }

		public List<Collision> CollisionsWithPositiveSet { get; set; }
		public List<Collision> CollisionsWithNegativeSet { get; set; }
	}

	public delegate void FindBestWindowCallback(FindBestWindowStatus status);

	/// <summary>
	/// Lugar de colision
	/// </summary>
	public class WindowCollision
	{
		public int TestingSize { get; set; }
		public sequence Sequence { get; set; }
		public int Site1 { get; set; }
		public int Site2 { get; set; }

		public string Pattern1 { get { return Sequence.content.Substring(Site1, TestingSize); } }
		public string Pattern2 { get { return Sequence.content.Substring(Site2, TestingSize); } }
	}

	/// <summary>
	/// Resultado de optimizacion de tamaño minimo de subcadena que no se repite
	/// </summary>
	public class FindMinSubstringSizeResult
	{
		public FindMinSubstringSizeResult()
		{
			Collisions = new List<WindowCollision>();
		}

		public int? Length { get; set; }
		public List<WindowCollision> Collisions { get; set; }
	}

	/// <summary>
	/// Indica el estado de ejecucion de la busqueda
	/// </summary>
	public class FindBestWindowStatus
	{
		public int PrefixLength { get; set; }
		public int SufixLength { get; set; }
		public int Count { get; set; }
		public int Total { get; set; }
		public List<Collision> CollisionsWithPositiveSet { get; set; }
		public List<Collision> CollisionsWithNegativeSet { get; set; }
		public bool StopNow { get; set; }
		public bool Stop { get; set; }
		public bool TakeCurrent { get; set; }
	}

	public static class WindowOptimizer
	{
		public static IEnumerable<FindBestWindowResult> FindBestWindow(
			IEnumerable<sequence> sequences,
			int minPrefix, int maxPrefix,
			int minPostfix, int maxPostfix,
			int siteIndex, bool breakOnCollision,
			FindBestWindowCallback cb = null)
		{
			var result = new List<FindBestWindowResult>();
			var status = new FindBestWindowStatus();

			var minSize = minPrefix + minPostfix;
			var maxSize = maxPrefix + maxPostfix;
			if (minSize < 2) minSize = 2;
			status.Total = maxSize - minSize + 1;
			int count = 0;
			for (int size = minSize; size <= maxSize; size++, count++)
			{
				var cap = minPrefix + minPostfix <= size;
				if (!cap) continue;
				var posibleMaxI = size - minPostfix;
				var MaxI = posibleMaxI >= maxPrefix ? posibleMaxI : maxPrefix;
				var requestStop = false;
				for (int i = minPrefix; i <= MaxI; i++)
				{
					var j = size - i;
					if (j >= minPostfix && j <= maxPostfix)
					{
						var collisionsp = new List<Collision>();
						var collisionsn = new List<Collision>();
						status.CollisionsWithNegativeSet = collisionsn;
						status.CollisionsWithPositiveSet = collisionsp;
						var posSamples = new Dictionary<string, Sample>();
						var negSamples = new Dictionary<string, Sample>();
						var wnd = SampleWindow.New(i, j);
						var hasCollisions = SampleCollector.CollectSamples(sequences, posSamples, negSamples, collisionsp, collisionsn, wnd, breakOnCollision, siteIndex);
						
						if (cb != null)
						{
							status.Count = count;
							status.PrefixLength = i;
							status.SufixLength = j;
							status.TakeCurrent = false;
							cb(status);
						}

						if (status.TakeCurrent || !hasCollisions)
						{
							var res = new FindBestWindowResult();
							res.PrefixLength = i;
							res.PostfixLength = j;
							res.CollisionsWithNegativeSet = collisionsn;
							res.CollisionsWithPositiveSet = collisionsp;
							result.Add(res);
						}

						if (status.StopNow) return result;

						if (status.Stop) requestStop = true;
					}
				}
				if (requestStop) return result;
			}

			return result;
		}

		public static FindMinSubstringSizeResult FindMinSubstringSize(IEnumerable<sequence> sequences, int beginSize, int maxSize)
		{
			var result = new FindMinSubstringSizeResult();
			var pdata = new Dictionary<string, int>();
			for (int size = beginSize; size < maxSize; size++)
			{
				result.Collisions.Clear();
				bool hasCollision = false;
				foreach (var sequence in sequences)
				{
					pdata.Clear();
					for (int i = 0; i < sequence.content.Length - size; i++)
					{
						var str = sequence.content.Substring(i, size);
						if (!pdata.ContainsKey(str))
						{
							pdata.Add(str, i);
						}
						else
						{
							var collision = new WindowCollision() { Site1 = pdata[str], Site2 = i, TestingSize = size, Sequence = sequence };
							result.Collisions.Add(collision);
							hasCollision = true;
						}
					}
				}
				if (!hasCollision)
				{
					result.Length = size;
					return result;
				}
			}
			return result;
		}
	}
}
